/********************************************************************************/
/*										*/
/*			Managing and accessing the hierarchy-related values   	*/
/*			     Written by Ken Goldman				*/
/*		       IBM Thomas J. Watson Research Center			*/
/*										*/
/*  Licenses and Notices							*/
/*										*/
/*  1. Copyright Licenses:							*/
/*										*/
/*  - Trusted Computing Group (TCG) grants to the user of the source code in	*/
/*    this specification (the "Source Code") a worldwide, irrevocable, 		*/
/*    nonexclusive, royalty free, copyright license to reproduce, create 	*/
/*    derivative works, distribute, display and perform the Source Code and	*/
/*    derivative works thereof, and to grant others the rights granted herein.	*/
/*										*/
/*  - The TCG grants to the user of the other parts of the specification 	*/
/*    (other than the Source Code) the rights to reproduce, distribute, 	*/
/*    display, and perform the specification solely for the purpose of 		*/
/*    developing products based on such documents.				*/
/*										*/
/*  2. Source Code Distribution Conditions:					*/
/*										*/
/*  - Redistributions of Source Code must retain the above copyright licenses, 	*/
/*    this list of conditions and the following disclaimers.			*/
/*										*/
/*  - Redistributions in binary form must reproduce the above copyright 	*/
/*    licenses, this list of conditions	and the following disclaimers in the 	*/
/*    documentation and/or other materials provided with the distribution.	*/
/*										*/
/*  3. Disclaimers:								*/
/*										*/
/*  - THE COPYRIGHT LICENSES SET FORTH ABOVE DO NOT REPRESENT ANY FORM OF	*/
/*  LICENSE OR WAIVER, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, WITH	*/
/*  RESPECT TO PATENT RIGHTS HELD BY TCG MEMBERS (OR OTHER THIRD PARTIES)	*/
/*  THAT MAY BE NECESSARY TO IMPLEMENT THIS SPECIFICATION OR OTHERWISE.		*/
/*  Contact TCG Administration (admin@trustedcomputinggroup.org) for 		*/
/*  information on specification licensing rights available through TCG 	*/
/*  membership agreements.							*/
/*										*/
/*  - THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED 	*/
/*    WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR 	*/
/*    FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OR 		*/
/*    NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS, OR ANY WARRANTY 		*/
/*    OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION OR SAMPLE.		*/
/*										*/
/*  - Without limitation, TCG and its members and licensors disclaim all 	*/
/*    liability, including liability for infringement of any proprietary 	*/
/*    rights, relating to use of information in this specification and to the	*/
/*    implementation of this specification, and TCG disclaims all liability for	*/
/*    cost of procurement of substitute goods or services, lost profits, loss 	*/
/*    of use, loss of data or any incidental, consequential, direct, indirect, 	*/
/*    or special damages, whether under contract, tort, warranty or otherwise, 	*/
/*    arising in any way out of use or reliance upon this specification or any 	*/
/*    information herein.							*/
/*										*/
/*  (c) Copyright IBM Corp. and others, 2016 - 2023				*/
/*										*/
/********************************************************************************/

//** Introduction
// This file contains the functions used for managing and accessing the
// hierarchy-related values.

//** Includes

#include "Tpm.h"

//**HIERARCHY_MODIFIER_TYPE
// This enumerates the possible hierarchy modifiers.
typedef enum
{
    HM_NONE = 0,
    HM_FW_LIMITED,  // Hierarchy is firmware-limited.
    HM_SVN_LIMITED  // Hierarchy is SVN-limited.
} HIERARCHY_MODIFIER_TYPE;

//*** HIERARCHY_MODIFIER Structure
// A HIERARCHY_MODIFIER structure holds metadata about an OBJECT's
// hierarchy modifier.
typedef struct HIERARCHY_MODIFIER
{
    HIERARCHY_MODIFIER_TYPE type;  // The type of modification.
    uint16_t min_svn;  // The minimum SVN to which the hierarchy is limited.
                       // Only valid if 'type' is HM_SVN_LIMITED.
} HIERARCHY_MODIFIER;

//** Functions

//*** HierarchyPreInstall()
// This function performs the initialization functions for the hierarchy
// when the TPM is simulated. This function should not be called if the
// TPM is not in a manufacturing mode at the manufacturer, or in a simulated
// environment.
void HierarchyPreInstall_Init(void)
{
    // Allow lockout clear command
    gp.disableClear = FALSE;

    // Initialize Primary Seeds
    gp.EPSeed.t.size = sizeof(gp.EPSeed.t.buffer);
    gp.SPSeed.t.size = sizeof(gp.SPSeed.t.buffer);
    gp.PPSeed.t.size = sizeof(gp.PPSeed.t.buffer);
#if(defined USE_PLATFORM_EPS) && (USE_PLATFORM_EPS != NO)
    _plat__GetEPS(gp.EPSeed.t.size, gp.EPSeed.t.buffer);
#else
    CryptRandomGenerate(gp.EPSeed.t.size, gp.EPSeed.t.buffer);
#endif
    CryptRandomGenerate(gp.SPSeed.t.size, gp.SPSeed.t.buffer);
    CryptRandomGenerate(gp.PPSeed.t.size, gp.PPSeed.t.buffer);

    gp.EPSeedCompatLevel = RuntimeProfileGetSeedCompatLevel();   // libtpms added begin
    gp.SPSeedCompatLevel = RuntimeProfileGetSeedCompatLevel();
    gp.PPSeedCompatLevel = RuntimeProfileGetSeedCompatLevel();   // libtpms added end
    // Initialize owner, endorsement and lockout authorization
    gp.ownerAuth.t.size       = 0;
    gp.endorsementAuth.t.size = 0;
    gp.lockoutAuth.t.size     = 0;

    // Initialize owner, endorsement, and lockout policy
    gp.ownerAlg                 = TPM_ALG_NULL;
    gp.ownerPolicy.t.size       = 0;
    gp.endorsementAlg           = TPM_ALG_NULL;
    gp.endorsementPolicy.t.size = 0;
    gp.lockoutAlg               = TPM_ALG_NULL;
    gp.lockoutPolicy.t.size     = 0;

    // Initialize ehProof, shProof and phProof
    gp.phProof.t.size = sizeof(gp.phProof.t.buffer);
    gp.shProof.t.size = sizeof(gp.shProof.t.buffer);
    gp.ehProof.t.size = sizeof(gp.ehProof.t.buffer);
    CryptRandomGenerate(gp.phProof.t.size, gp.phProof.t.buffer);
    CryptRandomGenerate(gp.shProof.t.size, gp.shProof.t.buffer);
    CryptRandomGenerate(gp.ehProof.t.size, gp.ehProof.t.buffer);

    // Write hierarchy data to NV
    NV_SYNC_PERSISTENT(disableClear);
    NV_SYNC_PERSISTENT(EPSeed);
    NV_SYNC_PERSISTENT(SPSeed);
    NV_SYNC_PERSISTENT(PPSeed);
    NV_SYNC_PERSISTENT(EPSeedCompatLevel);  // libtpms added begin
    NV_SYNC_PERSISTENT(SPSeedCompatLevel);
    NV_SYNC_PERSISTENT(PPSeedCompatLevel);  // libtpms added end
    NV_SYNC_PERSISTENT(ownerAuth);
    NV_SYNC_PERSISTENT(endorsementAuth);
    NV_SYNC_PERSISTENT(lockoutAuth);
    NV_SYNC_PERSISTENT(ownerAlg);
    NV_SYNC_PERSISTENT(ownerPolicy);
    NV_SYNC_PERSISTENT(endorsementAlg);
    NV_SYNC_PERSISTENT(endorsementPolicy);
    NV_SYNC_PERSISTENT(lockoutAlg);
    NV_SYNC_PERSISTENT(lockoutPolicy);
    NV_SYNC_PERSISTENT(phProof);
    NV_SYNC_PERSISTENT(shProof);
    NV_SYNC_PERSISTENT(ehProof);

    return;
}

//*** HierarchyStartup()
// This function is called at TPM2_Startup() to initialize the hierarchy
// related values.
BOOL HierarchyStartup(STARTUP_TYPE type  // IN: start up type
)
{
    // phEnable is SET on any startup
    g_phEnable = TRUE;

    // Reset platformAuth, platformPolicy; enable SH and EH at TPM_RESET and
    // TPM_RESTART
    if(type != SU_RESUME)
    {
        gc.platformAuth.t.size   = 0;
        gc.platformPolicy.t.size = 0;
        gc.platformAlg           = TPM_ALG_NULL;

        // enable the storage and endorsement hierarchies and the platformNV
        gc.shEnable = gc.ehEnable = gc.phEnableNV = TRUE;
    }

    // nullProof and nullSeed are updated at every TPM_RESET
    if((type != SU_RESTART) && (type != SU_RESUME))
    {
        gr.nullProof.t.size = sizeof(gr.nullProof.t.buffer);
        CryptRandomGenerate(gr.nullProof.t.size, gr.nullProof.t.buffer);
        gr.nullSeed.t.size = sizeof(gr.nullSeed.t.buffer);
        CryptRandomGenerate(gr.nullSeed.t.size, gr.nullSeed.t.buffer);
        gr.nullSeedCompatLevel = RuntimeProfileGetSeedCompatLevel();  // libtpms added
    }

    return TRUE;
}

//*** DecomposeHandle()
// This function extracts the base hierarchy and modifier from a given handle.
// Returns the base hierarchy.
static TPMI_RH_HIERARCHY DecomposeHandle(TPMI_RH_HIERARCHY   handle,   // IN
                                         HIERARCHY_MODIFIER* modifier  // OUT
)
{
    TPMI_RH_HIERARCHY base_hierarchy = handle;

    modifier->type                   = HM_NONE;

    // See if the handle is firmware-bound.
    switch(handle)
    {
        case TPM_RH_FW_OWNER:
        {
            modifier->type = HM_FW_LIMITED;
            base_hierarchy = TPM_RH_OWNER;
            break;
        }
        case TPM_RH_FW_ENDORSEMENT:
        {
            modifier->type = HM_FW_LIMITED;
            base_hierarchy = TPM_RH_ENDORSEMENT;
            break;
        }
        case TPM_RH_FW_PLATFORM:
        {
            modifier->type = HM_FW_LIMITED;
            base_hierarchy = TPM_RH_PLATFORM;
            break;
        }
        case TPM_RH_FW_NULL:
        {
            modifier->type = HM_FW_LIMITED;
            base_hierarchy = TPM_RH_NULL;
            break;
        }
    }

    if(modifier->type == HM_FW_LIMITED)
    {
        return base_hierarchy;
    }

    // See if the handle is SVN-bound.
    switch(handle & 0xFFFF0000)
    {
        case TPM_RH_SVN_OWNER_BASE:
            modifier->type = HM_SVN_LIMITED;
            base_hierarchy = TPM_RH_OWNER;
            break;
        case TPM_RH_SVN_ENDORSEMENT_BASE:
            modifier->type = HM_SVN_LIMITED;
            base_hierarchy = TPM_RH_ENDORSEMENT;
            break;
        case TPM_RH_SVN_PLATFORM_BASE:
            modifier->type = HM_SVN_LIMITED;
            base_hierarchy = TPM_RH_PLATFORM;
            break;
        case TPM_RH_SVN_NULL_BASE:
            modifier->type = HM_SVN_LIMITED;
            base_hierarchy = TPM_RH_NULL;
            break;
    }

    if(modifier->type == HM_SVN_LIMITED)
    {
        modifier->min_svn = handle & 0x0000FFFF;
        return base_hierarchy;
    }

    // Handle is neither FW- nor SVN-bound; return it unmodified.
    return handle;
}

//*** GetAdditionalSecret()
// Retrieve the additional secret for the given hierarchy modifier, along with the
// label that should be used when mixing the secret into a KDF. If the hierarchy
// needs no additional secret, secret_buffer's size is set to zero and secret_label
// is set to NULL.
//
//  Return Type: TPM_RC
//      TPM_RC_FW_LIMITED       The requested hierarchy is FW-limited, but the TPM
//                              does not support FW-limited objects or the TPM failed
//                              to derive the Firmware Secret.
//      TPM_RC_SVN_LIMITED      The requested hierarchy is SVN-limited, but the TPM
//                              does not support SVN-limited objects or the TPM failed
//                              to derive the Firmware SVN Secret for the requested
//                              SVN.
static TPM_RC GetAdditionalSecret(const HIERARCHY_MODIFIER* modifier,       // IN
                                  TPM2B_SEED*               secret_buffer,  // OUT
                                  const TPM2B**             secret_label    // OUT
)
{
    switch(modifier->type)
    {
        case HM_FW_LIMITED:
        {
#if FW_LIMITED_SUPPORT
            if(_plat__GetTpmFirmwareSecret(sizeof(secret_buffer->t.buffer),
                                           secret_buffer->t.buffer,
                                           &secret_buffer->t.size)
               != 0)
            {
                return TPM_RC_FW_LIMITED;
            }

            *secret_label = HIERARCHY_FW_SECRET_LABEL;
            break;
#else
            return TPM_RC_FW_LIMITED;
#endif  // FW_LIMITED_SUPPORT
        }
        case HM_SVN_LIMITED:
        {
#if SVN_LIMITED_SUPPORT
            if(_plat__GetTpmFirmwareSvnSecret(modifier->min_svn,
                                              sizeof(secret_buffer->t.buffer),
                                              secret_buffer->t.buffer,
                                              &secret_buffer->t.size)
               != 0)
            {
                return TPM_RC_SVN_LIMITED;
            }

            *secret_label = HIERARCHY_SVN_SECRET_LABEL;
            break;
#else
            return TPM_RC_SVN_LIMITED;
#endif  // SVN_LIMITED_SUPPORT
        }
        case HM_NONE:
        default:
        {
            secret_buffer->t.size = 0;
            *secret_label         = NULL;
            break;
        }
    }

    return TPM_RC_SUCCESS;
}

//***MixAdditionalSecret()
// This function obtains the additional secret for the hierarchy and
// mixes it into the base secret. The output buffer must have the same
// capacity as the base secret. The output buffer's size is set to the
// base secret size. If no additional secret is needed, the base secret
// is copied to the output buffer.
//
//  Return Type: TPM_RC
//      TPM_RC_FW_LIMITED       The requested hierarchy is FW-limited, but the TPM
//                              does not support FW-limited objects or the TPM failed
//                              to derive the Firmware Secret.
//      TPM_RC_SVN_LIMITED      The requested hierarchy is SVN-limited, but the TPM
//                              does not support SVN-limited objects or the TPM failed
//                              to derive the Firmware SVN Secret for the requested
//                              SVN.
static TPM_RC MixAdditionalSecret(const HIERARCHY_MODIFIER* modifier,           // IN
                                  const TPM2B*              base_secret_label,  // IN
                                  const TPM2B*              base_secret,        // IN
                                  TPM2B*                    output_secret       // OUT
)
{
    TPM_RC       result = TPM_RC_SUCCESS;
    TPM2B_SEED   additional_secret;
    const TPM2B* additional_secret_label = NULL;

    result =
        GetAdditionalSecret(modifier, &additional_secret, &additional_secret_label);
    if(result != TPM_RC_SUCCESS)
        return result;

    output_secret->size = base_secret->size;

    if(additional_secret.b.size == 0)
    {
        memcpy(output_secret->buffer, base_secret->buffer, base_secret->size);
    }
    else
    {
        CryptKDFa(CONTEXT_INTEGRITY_HASH_ALG,
                  base_secret,
                  base_secret_label,
                  &additional_secret.b,
                  additional_secret_label,
                  base_secret->size * 8,
                  output_secret->buffer,
                  NULL,
                  FALSE);
    }

    MemorySet(additional_secret.b.buffer, 0, additional_secret.b.size);

    return TPM_RC_SUCCESS;
}

//*** HierarchyGetProof()
// This function derives the proof value associated with a hierarchy. It returns a
// buffer containing the proof value.
TPM_RC HierarchyGetProof(TPMI_RH_HIERARCHY hierarchy,  // IN: hierarchy constant
                         TPM2B_PROOF*      proof       // OUT: proof buffer
)
{
    TPM2B_PROOF*       base_proof = NULL;
    HIERARCHY_MODIFIER modifier;

    switch(DecomposeHandle(hierarchy, &modifier))
    {
        case TPM_RH_PLATFORM:
            // phProof for TPM_RH_PLATFORM
            base_proof = &gp.phProof;
            break;
        case TPM_RH_ENDORSEMENT:
            // ehProof for TPM_RH_ENDORSEMENT
            base_proof = &gp.ehProof;
            break;
        case TPM_RH_OWNER:
            // shProof for TPM_RH_OWNER
            base_proof = &gp.shProof;
            break;
        default:
            // nullProof for TPM_RH_NULL or anything else
            base_proof = &gr.nullProof;
            break;
    }

    return MixAdditionalSecret(
        &modifier, HIERARCHY_PROOF_SECRET_LABEL, &base_proof->b, &proof->b);
}

//*** HierarchyGetPrimarySeed()
// This function derives the primary seed of a hierarchy.
TPM_RC HierarchyGetPrimarySeed(TPMI_RH_HIERARCHY hierarchy,  // IN: hierarchy
                               TPM2B_SEED*       seed        // OUT: seed buffer
)
{
    TPM2B_SEED*        base_seed = NULL;
    HIERARCHY_MODIFIER modifier;

    switch(DecomposeHandle(hierarchy, &modifier))
    {
        case TPM_RH_PLATFORM:
            base_seed = &gp.PPSeed;
            break;
        case TPM_RH_OWNER:
            base_seed = &gp.SPSeed;
            break;
        case TPM_RH_ENDORSEMENT:
            base_seed = &gp.EPSeed;
            break;
        default:
            base_seed = &gr.nullSeed;
            break;
    }

    return MixAdditionalSecret(
        &modifier, HIERARCHY_SEED_SECRET_LABEL, &base_seed->b, &seed->b);
}

// libtpms added begin
SEED_COMPAT_LEVEL
HierarchyGetPrimarySeedCompatLevel(
                   TPMI_RH_HIERARCHY    hierarchy     // IN: hierarchy
                       )
{
    HIERARCHY_MODIFIER modifier;

    switch(DecomposeHandle(hierarchy, &modifier))
    {
      case TPM_RH_PLATFORM:
        return gp.PPSeedCompatLevel;
        break;
      case TPM_RH_OWNER:
        return gp.SPSeedCompatLevel;
        break;
      case TPM_RH_ENDORSEMENT:
        return gp.EPSeedCompatLevel;
        break;
      case TPM_RH_NULL:
        return gr.nullSeedCompatLevel;
      default:
        return RuntimeProfileGetSeedCompatLevel();
        break;
    }
}
// libtpms added end

//*** ValidateHierarchy()
// This function ensures a given hierarchy is valid and enabled.
//  Return Type: TPM_RC
//      TPM_RC_HIERARCHY        Hierarchy is disabled
//      TPM_RC_FW_LIMITED       The requested hierarchy is FW-limited, but the TPM
//                              does not support FW-limited objects.
//      TPM_RC_SVN_LIMITED      The requested hierarchy is SVN-limited, but the TPM
//                              does not support SVN-limited objects or the given SVN
//                              is greater than the TPM's current SVN.
//      TPM_RC_VALUE            Hierarchy is not valid
TPM_RC ValidateHierarchy(TPMI_RH_HIERARCHY hierarchy  // IN: hierarchy
)
{
    BOOL               enabled;
    HIERARCHY_MODIFIER modifier;

    hierarchy = DecomposeHandle(hierarchy, &modifier);

    // Modifier-specific checks.
    switch(modifier.type)
    {
        case HM_NONE:
            break;
        case HM_FW_LIMITED:
        {
#if FW_LIMITED_SUPPORT
            break;
#else
            return TPM_RC_FW_LIMITED;
#endif  // FW_LIMITED_SUPPORT
        }
        case HM_SVN_LIMITED:
        {
#if SVN_LIMITED_SUPPORT
            // SVN-limited hierarchies are only enabled for SVNs less than or
            // equal to the current firmware's SVN.
            if(modifier.min_svn > _plat__GetTpmFirmwareSvn())
            {
                return TPM_RC_SVN_LIMITED;
            }
            break;
#else
            return TPM_RC_SVN_LIMITED;
#endif  // SVN_LIMITED_SUPPORT
        }
    }

    switch(hierarchy)
    {
        case TPM_RH_PLATFORM:
            enabled = g_phEnable;
            break;
        case TPM_RH_OWNER:
            enabled = gc.shEnable;
            break;
        case TPM_RH_ENDORSEMENT:
            enabled = gc.ehEnable;
            break;
        case TPM_RH_NULL:
            enabled = TRUE;
            break;
        default:
            return TPM_RC_VALUE;
    }

    return enabled ? TPM_RC_SUCCESS : TPM_RC_HIERARCHY;
}

//*** HierarchyIsEnabled()
// This function checks to see if a hierarchy is enabled.
// NOTE: The TPM_RH_NULL hierarchy is always enabled.
//  Return Type: BOOL
//      TRUE(1)         hierarchy is enabled
//      FALSE(0)        hierarchy is disabled
BOOL HierarchyIsEnabled(TPMI_RH_HIERARCHY hierarchy  // IN: hierarchy
)
{
    return ValidateHierarchy(hierarchy) == TPM_RC_SUCCESS;
}

//*** HierarchyNormalizeHandle
// This function accepts a handle that may or may not be FW- or SVN-bound,
// and returns the base hierarchy to which the handle refers.
TPMI_RH_HIERARCHY HierarchyNormalizeHandle(TPMI_RH_HIERARCHY handle  // IN: handle
)
{
    HIERARCHY_MODIFIER unused_modifier;

    return DecomposeHandle(handle, &unused_modifier);
}

//*** HierarchyIsFirmwareLimited
// This function accepts a hierarchy handle and returns whether it is firmware-
// limited.
BOOL HierarchyIsFirmwareLimited(TPMI_RH_HIERARCHY handle  // IN
)
{
    HIERARCHY_MODIFIER modifier;

    DecomposeHandle(handle, &modifier);

    return modifier.type == HM_FW_LIMITED;
}
//*** HierarchyIsSvnLimited
// This function accepts a hierarchy handle and returns whether it is SVN-
// limited.
BOOL HierarchyIsSvnLimited(TPMI_RH_HIERARCHY handle  // IN
)
{
    HIERARCHY_MODIFIER modifier;

    DecomposeHandle(handle, &modifier);

    return modifier.type == HM_SVN_LIMITED;
}
